#include "algorithm.h"

#include "..\gui\feedframehandler.h"
#include "..\gui\gangiafeeddialog.h"


Algorithm::Algorithm(void)
{
	ChNumber = 0;
	MainWindow	=	NULL;
	SecWindow	=	NULL;
	MainWinLen	=	0;
	SecWinLen	=	0;
	EnableFeedback = true;
	EnableClassification = true;
	SocketLen = 0;
	memset(&AlgoPerf,0,sizeof(AlgoPerf));
	memset(&ShortTimer,0,sizeof(ShortTimer));
	memset(&MainTimer,0,sizeof(MainTimer));
	LastClassification	=	0;
	memset(FeedBack,0,sizeof(FeedBack));
	pOutputWindow = NULL;
	SingleSample=false;
}

Algorithm::~Algorithm(void)
{

	Close();

	if(pOutputWindow != NULL)
	{
		pOutputWindow->Hide();
		pOutputWindow->Destr();
	}
	pOutputWindow = NULL;



}

void Algorithm::StartCounter(TimerParams *pParams)
{
	pParams->Begin = ::wxGetLocalTimeMillis();
}

void Algorithm::StopCounter(TimerParams *pParams)
{
	wxLongLong	Time;
	
	Time = ::wxGetLocalTimeMillis() - pParams->Begin;
	pParams->Accumulator= (pParams->Accumulator * 15+ Time.ToDouble())/16;
	pParams->Count++;
	
}
void Algorithm::StartMainTimeCounter(void)
{
	wxLongLong	Time;
	AlgoPerf.CountMain++;
	Time = ::wxGetLocalTimeMillis() - MainTimer.Begin;
	if(Time.ToDouble() > 0) AlgoPerf.FreqMain =1000/Time.ToDouble();
	
	StartCounter(&MainTimer);
}
void Algorithm::StopMainTimeCounter(void)
{

	StopCounter(&MainTimer);
	AlgoPerf.TimeMain = MainTimer.Accumulator;
}
void Algorithm::StartSecTimeCounter(void)
{
	wxLongLong	Time;
	AlgoPerf.CountSec++;
	Time = ::wxGetLocalTimeMillis() - ShortTimer.Begin;
	if(Time.ToDouble() != 0) AlgoPerf.FreqSec = 1000/Time.ToDouble();
	StartCounter(&ShortTimer);
}
void Algorithm::StopSecTimeCounter(void)
{
	StopCounter(&ShortTimer);
	AlgoPerf.TimeSec = ShortTimer.Accumulator;
}
void Algorithm::Close(void)
{
	if(MainWindow != NULL)
	{
		delete [] MainWindow;
		delete [] SecWindow;
		if(c4mMainWindow) delete c4mMainWindow;
		if(c4mSecWindow) delete c4mSecWindow;
	}
}



void Algorithm::Open(int MLen,int SLen,int NumerOfValidDataChannels,int Feedback,int FeedbackGUI,int AlgorithmFrequency)
{	
	MainWinLen		=	MLen;
	SecWinLen		=	SLen;
	if(NumerOfValidDataChannels > MAX_CHANNELS) 
	{
		wxMessageBox(wxString::Format(_T("Error NumerOfValidDataChannels must be smaller than %d"),MAX_CHANNELS));
		return;
	}
	ChNumber			=	NumerOfValidDataChannels;
		
	MainWindow		=	new	float[MainWinLen*ChNumber];
	SecWindow		=	new	float[SecWinLen*ChNumber];

	FeedLen=Feedback; 
	LastClassification=0;
	MainCursor = SecCursor = 0;
	memset(MainWindow,0,sizeof(float)*MainWinLen*ChNumber);
	memset(SecWindow,0,sizeof(float)*SecWinLen*ChNumber);

	c4mSecWindow	=	new BCIMatrix(SecWinLen,ChNumber);
	c4mMainWindow	=	new BCIMatrix(MainWinLen,ChNumber);
	
	
	switch(FeedbackGUI)
	{
	case 0:
		pOutputWindow = new GangiaFeedDialog(NULL,Feedback);
		break;
	case 1:
		pOutputWindow = new FeedFrameHandler(NULL,99,Feedback,AlgorithmFrequency,20);
		break;
	
	}
}
void Algorithm::SendConfiguration(void)
{

}
void Algorithm::RunMainWin(void)
{
	MainCursor = 0;
	::wxMessageBox(_("This function should be implemented or deactivated by setting MLen = 0 in the opening function"));
}
void Algorithm::RunSecWin(void)
{
	SecCursor = 0;
	::wxMessageBox(_("This function should be implemented or deactivated by setting SLen = 0 in the opening function"));
}

void Algorithm::RunSingleSample(void)
{
	::wxMessageBox(_("This function should be implemented or deactivated by setting SingleSample = false in the opening function"));
}

void Algorithm::Restart(void)
{
	wxCriticalSection Region;
	Region.Enter();
	memset(&AlgoPerf,0,sizeof(AlgoPerf));
	memset(&ShortTimer,0,sizeof(ShortTimer));
	memset(&MainTimer,0,sizeof(MainTimer));

	LastClassification	=	0;
	memset(FeedBack,0,sizeof(FeedBack));
	//FeedLen=0; 
	MainCursor = SecCursor = 0;
	memset(MainWindow,0,sizeof(float)*MainWinLen*ChNumber);
	memset(SecWindow,0,sizeof(float)*SecWinLen*ChNumber);
	Region.Leave();
}

	
void Algorithm::PutSample(SampleStruct *pData)
{
	int Number;
	wxCriticalSection Region;
	Region.Enter();
	if(MainWinLen == 0) return;
	if(MainCursor > MainWinLen) wxMessageBox(_T("Algorithm::PutSample Error. MainCursor OUTSIDE WINDOW!"));
	if(SecCursor > SecWinLen)	wxMessageBox(_T("Algorithm::PutSample Error. SecCursor OUTSIDE WINDOW!"));

	Number = (ChNumber > pData->Data.DataLen)?pData->Data.DataLen: ChNumber;

	for(int i=0;i<Number;i++)
	{
		if(MainWinLen!=0)	MainWindow[MainCursor + i * MainWinLen]		=	pData->Data.Signal[i];
		if(SecWinLen!=0)	SecWindow[SecCursor + i * SecWinLen]	=	pData->Data.Signal[i];
	}
	
	//If MainWinLen!=0 and the MainWin is full, eecute RunMainWin to elaborate window
	if(MainWinLen != 0)
	{
		MainCursor++;
		if(MainCursor == MainWinLen) 
		{
			memcpy(c4mMainWindow->pdata,MainWindow,sizeof(float32)*MainWinLen*ChNumber);
			StartMainTimeCounter();
			RunMainWin();
			StopMainTimeCounter();
		}
	}
	//If SecWinLen!=0 and the SecWin is full, eecute RunSecWin to elaborate window
	if(SecWinLen != 0)
	{
		SecCursor++;
		if(SecCursor == SecWinLen) 
		{
			memcpy(c4mSecWindow->pdata,SecWindow,sizeof(float32)*SecWinLen*ChNumber);
			StartSecTimeCounter();
			RunSecWin();
			StopSecTimeCounter();
		}
	}

	//Run RunSingleSample
	if (SingleSample)
		RunSingleSample();

	pOutputWindow->SetAlgoStats(AlgoPerf);
	pOutputWindow->SetTrigger(pData->Events.HIMTrigg,pData->Events.AEnimaTrigg,pData->Events.AEnimaTrigg);
	pData->Algo.Classification= GetLastClassification();
	memcpy(pData->Algo.Feedback,FeedBack,FeedLen * sizeof(float));
	pData->Algo.FeedLen	=	FeedLen;
	

	Region.Leave();
}

void	Algorithm::ShiftMainWin(int Len)
{
	wxCriticalSection Region;
	Region.Enter();
	if(Len >= MainCursor) 
	{
		MainCursor = 0;
		return;
	}
	for(int i=0;i<ChNumber;i++)
	{
		memmove(&MainWindow[i * MainWinLen],&MainWindow[i * MainWinLen + Len],sizeof(float)*(MainCursor-Len));
	}
	MainCursor -= Len;
	Region.Leave();
}

void	Algorithm::ShiftSecondaryWin(int Len)
{
	wxCriticalSection Region;
	Region.Enter();
	if(Len >= SecCursor) 
	{
		SecCursor = 0;
		return;
	}
	for(int i=0;i<ChNumber;i++)
	{
		memmove(&SecWindow[i * SecWinLen],&SecWindow[i * SecWinLen + Len],sizeof(float)*(SecCursor-Len));
	}
	SecCursor -= Len;
	Region.Leave();
}

int	Algorithm::GetLastClassification(void)
{
	int Class = LastClassification;
	LastClassification = 0;
	return Class;
}
Algostats Algorithm::GetAlgorithmStatistics(void) {return AlgoPerf;}
//Restuisce i prim MaxLen valori di feedback
void	Algorithm::SetFeedback(float *pFeedback)
{
	wxCriticalSection Region;
	Region.Enter();
	BCIMessage Temp;
	if(pOutputWindow!=NULL)	pOutputWindow->SetFeedback(pFeedback);
	
	
	memset(FeedBack,0,sizeof(FeedBack));
	memcpy(FeedBack,pFeedback,sizeof(float)*FeedLen);	
	if(EnableFeedback)
	{
		Temp = ComposeMessage(FEEDBACK);
		for(int i=0;i<FeedLen;i++)Temp.Buffer[i]=floor(FeedBack[i]);
		WriteOut((char*)&Temp,sizeof(Temp));		
	}
	Region.Leave();
}
/*
void	Algorithm::GetProcessingParams(char *pAcqName,char *pClassName,bool *pEnableFeedback,bool *pEnableClassification)
{
	sprintf(pAcqName,"");
	sprintf(pClassName,"");
	*pEnableFeedback = 0;
	*pEnableClassification = 0;
}*/

void	Algorithm::SetClassification(int Class)
{
	wxCriticalSection Region;
	Region.Enter();
	BCIMessage Temp;
	LastClassification = Class;
	pOutputWindow->SetClassification(Class);
	if(EnableClassification)
	{
		Temp = ComposeMessage(CLASSIFICATION,Class);
		WriteOut((char*)&Temp,sizeof(Temp));		
//		if(sock->LastCount() != sizeof(Temp)) wxMessageBox(_("Socket error"));
	}
	//Inserire disegno
	Region.Leave();
}

void	Algorithm::WriteOut(char *pData,int Len)
{
	if((SocketLen+Len)>=2048)SocketLen =0;
	memcpy(&SocketBuffer[SocketLen],pData,Len);
	SocketLen += Len;
	
}
int Algorithm::GetData(char *pBuffer)
{
	int Ret=0;
	if(SocketLen > 0)
	{
		memcpy(pBuffer,SocketBuffer,SocketLen);
		Ret	=SocketLen;
		SocketLen = 0;
	}
	return Ret;
}

void	Algorithm::ToggleShow(void)
{
	if(pOutputWindow->IsShown())pOutputWindow->Hide();
	else pOutputWindow->Show();
/*#ifdef FEED_GUI
	if(pDialog->IsShown())pDialog->Hide();
	else pDialog->Show();
#else
	if(pFeedFrame->IsShown())pFeedFrame->Hide();
	else pFeedFrame->Show();
#endif*/
	
}

